/*           
           import   TASK1EPA,TASK2EPA  APPLICATION TASKS' START ADDRESSES
           import   TASK3EPA,TASK4EPA  DEFINED IN APP_T1, APP_T2 MODULES
           import   OPEN_D
           export     SVCNEW,SVCPRT,SVCDLY,SVCSND,SVCRCV,SVCRPLY
*/


/* 
*          68000 PROCESSOR DEFINITIONS
* 
* 
*/ 
     	   .EQU        ENABLE,0x2000     /* ENABLE INTERRUPT VALUE FOR SR    */
    	   .EQU        DISABLE,0x2700    /* DISABLE INTERRUPTS SR VALUE    */
/* 
*          VECTOR DEFINITIONS 
*/ 
    	   .EQU        TRAPVEC,0x80      /* TRAP O VECTOR ADDRESS    */
           .EQU        AL_VEC,0x64       /* AUTOVECTOR 1 LOCATION    */
/* 
*          HARDWARE LOCATIONS 
*/ 
           .EQU        POLL,0xFFC041     /* INTERRUPT POLL PORT    */ 
           .EQU        TERMOUT,0xFFC071  /* TERMINAL OUTPUT PORT    */
           .EQU        TERMCMD,0xFFC073  /* TERMINAL COMMAND PORT    */ 
/* 
*          TERMINAL COMMANDS
*/ 
           .EQU        TERMENB,0x25       /* OUTPUT INTERRUPT ENABLE    */
           .EQU        TERMDIS,0x24       /* OUTPUT INTERRUPT DISABLE     */
/* 
*          TIMER PORT 
*/ 
           .EQU        TIMER,0xFFC081    /* TIMER PORT BASE    */ 
           .EQU        T_MODE1,0x74      /* MODE BYTE #1    */ 
           .EQU        T_MODE2,0xB0      /* MODE BYTE #2    */
/* 
*          MISCELLANEOUS HARDWARE STUFF 
*/ 
          /* .EQU        RDVEC,0x0C   */      /* COMMAND TO READ VECTOR     */
          /* .EQU        CLRINT,0x20  */      /* COMMAND TO END INTERRUPT     */
/**********************************************************************
* 
*          L B O S   E R R O R   C O D E S
* 
*          THE FOLLOWING ERROR CODES CAN BE RETURNED IN %D0.L IF 
*          AN ERROR OCCURS DURING THE PROCESSING OF AN SVC. 
* 
**********************************************************************/
           .EQU        E_BADSVC,-1        /* ILLEGAL SVC CODE IN %D0    */
           .EQU        E_NOMCB,-2        /* NO MCB AVAILABLE FOR R.EQUEST    */
           .EQU        E_BADTASK,-3       /* ILLEGAL TASK NUMBER     */
           .EQU        E_BADMCB,-4        /* ILLEGAL MCB ADDRESS ON REPLY    */
/* 
**********************************************************************
* 
*          S V C   C O D E   E Q U A T E S
* 
*          THESE SYMBOLS ARE USED BY O/S TASKS FOR REFERENCING
*          THE SVC CODES
* 
**********************************************************************/
           .EQU        SVCNEW,0         /* NEW TASK SVC    */
           .EQU        SVCPRT,1         /* PRINT ON TERMINAL SVC     */
           .EQU        SVCDLY,2         /* DELAY SVC     */
           .EQU        SVCSND,3         /* SEND SVC    */
           .EQU        SVCRCV,4         /* RECEIVE SVC     */
           .EQU        SVCRPLY,5        /* REPLY SVC     */
/**********************************************************************
* 
*          T C B   (TASK CONTROL BLOCK) L A Y O U T
* 
*          EACH TASK IN THE LBOS SYSTEM IS REPRESENTED BY A DATA
*          STRUCTURE CALLED A "TASK CONTROL BLOCK". THIS AREA IS
*          USED BY OPERATING SYSTEM TO STORE TASK-SPECIFIC DATA.
* 
*          OFFSET               CONTENTS                        NAME
* 
*          0          POINTER TO NEXT TCB OR ZERO IF NO         T_LINK
*                     NEXT TCB
*          4          POINTER TO MESSAGES WAITING TO BE         T_MSG 
*                     RECEIVED
*          8          POINTER TO MESSAGES AWAITING REPLY        T_RPLY
*          12         CONTENTS OF TASK'S USP WHEN TASK IS       T_USP 
*                     WAITING 
*          16         CONTENTS OF TASK'S SSP WHEN TASK IS       T_SSP 
*                     WAITING 
*          20         TASK FLAGS WORD                           T_FLAG
*          24         STACK AREA FOR SYSTEM 
* 
*          THE TASK'S REGISTERS, PC, AND STATUS REGISTER ARE STORED 
*          ON THE SYSTEM STACK AREA WITHIN THE TCB WHEN THE TASK IS 
*          NOT RUNNING
* 
**********************************************************************/

/* 
*          ASSEMBLER DEFINITIONS
*/ 
    	   .EQU        STACKSIZE,0x100     /* SIZE OF SYSTEM STACK     */
/* 
*          TCB OFFSETS
*/ 
           .EQU        T_LINK,0         /* LINK WORD FOR TASK LIST THREAD    */
           .EQU        T_MSG,T_LINK+4  /* LINK TO WAITING MESSAGES    */
           .EQU        T_RPLY,T_MSG+4   /* LINK TO MESSAGE RECEIVED    */
           .EQU        T_USP,T_RPLY+4  /* USER STACK POINTER AREA     */
           .EQU        T_SSP,T_USP+4   /* SYSTEM STACK POINTER AREA     */
           .EQU        T_FLAG,T_SSP+4   /* FLAG WORD     */    
           .EQU        T_STACK,T_FLAG+4+STACKSIZE        /* STACK AREA    */
           .EQU        TCBSIZE,T_STACK   /* SIZE OF TCB IN BYTES    */
/* 
*          FLAG WORD BITS 
*/ 
           .EQU        IWAIT,0         /* WAITING ON INTERRUPT    */
           .EQU        MWAIT,1         /* WAITING ON MESSAGE COMPLETION    */ 
           .EQU        RWAIT,2         /* WAITING ON MESSAGE RECEIPT    */
/**********************************************************************
* 
*          M E S S A G E   C O N T R O L   B L O C K
* 
*          LBOS PERFORMS SYSTEM SERVICES BY ROUTING A MESSAGE FROM
*          THE R.EQUESTING TASK TO A TASK WHICH PERFORMS APPROPRIATE 
*          FUNCTION. EACH MESSAGE IS REPRESENTED BY A FIXED LENGTH
*          DATA AREA CALLED A "MESSAGE CONTROL BLOCK". THIS AREA
*          HAS THE FOLLOWING LAYOUT:
* 
*          OFFSET               CONTENTS                        NAME
* 
*          0          LINK TO NEXT MCB OR ZERO IF NOT           M_LINK
*                     NEXT MCB
*          4          BUFFER ADDRESS (IF R.EQUIRED)              M_BUFF
*          8          32-BIT COUNT (BYTE COUNT OR DELAY TIME)   M_COUNT
*          12         R.EQUESTING TCB ADDRESS                    M_RTCB
*          16         RETURNED STATUS                           M_STAT
* 
*          A MESSAGE IS SENT USING THE SEND SVC AND RECEIVED USING
*          THE RECEIVE SVC. THE ISSUING TASK IS SUSPENDED UNTIL THE 
*          SERVING TASK ISSUES A REPLY SVC. 
* 
**********************************************************************
* 
*          ASSEMBLER DEFINITIONS: 
*/ 
           .EQU        M_LINK,0         /* LINK WORD     */
           .EQU        M_BUFF,M_LINK+4  /* BUFFER ADDRESS FOR I/O    */
           .EQU        M_COUNT,M_BUFF+4  /* 32-BIT COUNT    */
           .EQU        M_RTCB,M_COUNT+4 /* REQUESTING TCB ADDRESS    */
           .EQU        M_STAT,M_RTCB+4  /* STATUS WORD     */
           .EQU        MCBSIZE,M_STAT+4  /* SIZE OF MCB     */
  
  
/**********************************************************************
* 
*          TIMER ADDRESSES ***?***
* 
* 
* 
**********************************************************************/

  

           .DATA                              /* UNITIALIZED DATA    */
TINDEX:    .SKIP      4                 /* TASK INDEX     */
CURRENT:   .SKIP      4                 /* ->CURRENT TASK'S TCB     */
TCBLIST:   .SKIP      4                 /* ->ALL TCB'S IN PRIORITY ORDER    */
/* 
*          TCB STORAGE AREA 
*/ 
TASK1:     .SKIP       TCBSIZE          /* TIMER TASK TCB AREA    */
TASK2:     .SKIP       TCBSIZE          /* TERMINAL TASK TCB AREA     */
TASK3:     .SKIP       TCBSIZE          /* APPLICATION TASK #1    */
TASK4:     .SKIP       TCBSIZE          /* APPLICATION TASK #2    */
/* 
*          MCB STORAGE AREA 
*/ 
MCBLIST:   .SKIP      4                 /* ->FREE LIST OF MCB'S    */ 
           .EQU       NMCBS,5           /* NUMBER OF FREE MCB'S    */ 
MCBAREA:   .SKIP      MCBSIZE*NMCBS     /* AREA FOR MCB'S     */

  
 /**********************************************************************
* 
*          L B O S   I N I T I A L I Z A T I O N   C O D E
* 
*          ROUTINE "INIT" PERFORMS INITIALIZATION PRIOR TO LBOS 
*          RECEIVING CONTROL. ACTIONS PERFORMED ARE:
* 
*          1).        INITIALIZE VECTOR AREA TO APPROPRIATE INTERRUPT
*                     ROUTINES
*          2).        INITIALIZE TCB AREA
*          3).        INITIALIZE MCB FREE LIST
*          4).        JUMP TO DISPATCHER
* 
**********************************************************************/
           .TEXT
                                          /* TIMER IRQ NO. ***?*** */
            .LONG       IRQSR 
INIT: 
           MOVE.W     #DISABLE,%SR        /* DISALLOW INTERRUPTS    */
           MOVE.L     #TRAP0,%D0
           MOVE.L     %D0,TRAPVEC         /* SET TRAP VECTOR    */

           MOVE.L     #0x3FFE,%A7         /* SET STACK POINTER ***?*** */
           CLR.L      TINDEX 
  
           LEA.L        INITDATA,%A0        /* %A0 -> TCB INIT AREA    */
           LEA.L        TCBLIST,%A3         /* %A2 -> "PREVIOUS TCB"    */
INITTCB:
           MOVE.L     (%A0)+,%A1           /* %A1 -> TCB AREA (UNINIT'ED)    */
           CMPA.L     #0,%A1              /* DONE YET?    */
           BEQ.W        TCBDONE           /*  EQ => DONE WITH TCB AREA    */
           CLR.L      T_USP(%A1)          /* CLEAR TASK'S USP    */
           LEA.L      T_STACK(%A1),%A2     /* %A2 -> END OF STACK AREA    */
  
           TST.W      (%A0)+              /* SKIP HIGH WORD OF VEC.OFF.    */
           MOVE.W     (%A0)+,-(%A2)       /*  PUSH VEC.OFF.    */
           CLR.L      T_FLAG(%A1)         /* MARK "DISPATCHABLE"    */
  
           MOVE.L     (%A0)+,-(%A2)       /*  PUSH TASK'S PC    */
           TST.W      (%A0)+              /* SKIP HIGH WORD OF SR    */
           MOVE.W     (%A0)+,-(%A2)        /* PUSH TASK'S SR    */
           MOVEM.L    %D0-%D7/%A0-%A6,(%A2)  /* SAVE "REGISTERS"   ***?*** */    
           MOVE.L     %A2,T_SSP(%A1)       /* SET KERNEL STACK    */
           MOVE.L     %A1,T_LINK(%A3)      /* SET PREVIOUS TCB LINK WORD    */
           MOVE.L     %A1,%A3              /* SAVE PREVIOUS TCB ADDRESS    */
           BRA.W      INITTCB              /* LOOP TILL DONE    */
TCBDONE:
           CLR.L      T_LINK(%A3)          /* NULL OUT LAST LINK WORD    */
/* 
*          INIT MESSAGE CONTROL BLOCK AREA
*/ 
           MOVE.L     #NMCBS,%D0           /* %D0 = NUMBER OF MCBS TO INIT    */
           LEA.L      MCBAREA,%A0          /* %A0 -> AREA    */
           MOVE.L     %A0,MCBLIST          /* INIT LIST HEAD    */
MCBLOOP:
           MOVE.L     %A0,%A1              /* SAVE CURRENT ADDRESS    */ 
           LEA        MCBSIZE(%A0),%A0     /* %A0 -> NEXT MCB    */
           MOVE.L     %A0,M_LINK(%A1)      /* SET LINK WORD    */
           SUBQ.L     #1,%D0
           BNE        MCBLOOP              /* REPEAT TILL DONE     */
           CLR.L      M_LINK(%A1)          /* SET LAST LINK WORD TO 0    */
/* 
*          TIMER INITIALIZATION ***?***
*/ 
           CLR.L      TSTATUS              /* INITIALIZE TIMER STATUS VARIABLE */
/* 
*          PREPARE TO ENTER DISPATCHER
*/ 
           CLR.L      CURRENT            /* NO CURRENT TASK    */
           JMP        DISPATCH           /* GO TO DISPATCHER     */
/**********************************************************************
* 
* 
*          TIMER IRQ SERVICE ROUTINE
*          ( DISPATCH ON EVERY IRQ )
* 
* 
**********************************************************************/
IRQSR:
           MOVEM.L    %D0-%D7/%A0-%A6,(%A7)  /* SAVE REGISTERS    */
           BRA        NEWTASK 
  
/**********************************************************************
* 
*          L B O S   C A L L I N G   C O N V E N T I O N S
* 
*          LBOS SUPPORTS 6 SVC'S (SUPERVISOR CALLS). AN SVC IS
*          R.EQUESTED BY LOADING THE APPROPRIATE FUNCTION CODE INTO
*          %D0.L AND EXECUTING A TRAP #0 INSTRUCTION. THE FOLLOWING
*          FUNCTIONS ARE AVAILABLE: 
* 
*          FUNCTION             %D0       %A0          %D1         %D2
* 
*          NEWTASK              0        N/A         N/A        N/A 
*          PRINTLINE ON         1        BUFFER      BYTE COUNT N/A 
*          TERMINAL                      ADDRESS
*          DELAY                2        N/A         DELAY      N/A 
*                                                    COUNT
*          SEND MESSAGE         3        BUFFER      COUNT      TASK #
*                                        ADDRESS
*          RECEIVE MESSAGE      4        N/A         N/A        N/A 
*          REPLY TO MESSAGE     5        MCB ADDRESS N/A        N/A 
* 
*          THE ENTRY POINT FOR SVC ROUTINES IS "TRAP0:".
* 
**********************************************************************/
TRAP0: 
           MOVEM.L    %D0-%D7/%A0-%A6,(%A7)        /* SAVE ALL REGISTERS    */
           CMP.L      #MAXSVC,%D0               /* IS %D0 IN RANGE?    */
           BHI.W      BADSVC                   /* IF HIS NO,RETURN ERROR     */
           LSL.L      #2,%D0                    /* MULTIPLAY %D0 BY 4    */
           LEA.L      SVCTAB,%A6                /* %A6->BEGINNING OF TABLE     */
           MOVE.L     0(%A6,%D0.L),%A6            /* %A6->SVC ROUTINE    */
           JMP        (%A6)                     /* ENTER SVC ROUTINE    */
/*   IF FUNCTION NUMBER OUT OF RANGE,RETURN -1.
*/ 
BADSVC:
           MOVEM.L    (%SP),%D0-%D7/%A0-%A6        /* RESTORE REGISTERS    */
           MOVE.L     #E_BADSVC,%D0             /* LOAD RETURN CODE     */
           RTE                                 /* RETURN TO TASK CODE    */
/* 
*   TABLE OF SVC ROUTINE ADDRESSES,IN FUNCTIO CODE ORDER
*/ 
  
SVCTAB:
           .LONG       NEWTASK            /* 0 ENTER DISPATCHER     */
           .LONG       PRTLINE            /* 1 TERMINAL OUTPUT    */
           .LONG       DELAY              /* 2 REAL-TIME DELAY    */
           .LONG       SEND               /* 3 SEND A MESSAGE    */ 
           .LONG       RECV               /* 4 RECEIVE A MEASSEAGE    */
           .LONG       REPLY              /* 5 REPLY TO MEASSEAGE    */ 
           .EQU        MAXSVC,(.-SVCTAB)/4 /* MAX SVC NUMBER + 1     */
/**********************************************************************
*  
*   N E W T A S K   S V C  R O U T I N E
* 
*   SVC "NEWTASK" IS USED TO SUSPEND THE EXECUTION OF THE PRESEND 
*   TASK AND SCHEDULE EXECUTION OF THE NEXT TASK.THIS ROUTINE IS
*   CALLED BY OTHER SVC ROUTINES WHEN THE CURRENT TASK BECOMES
*   NON-DISPETCHABLE. 
* 
*   ENTRY CONDITIONS: 
* 
*          REGISTERS AND PC/SR STORED ON THE STACK IN STANDARD ORDER. 
* 
*   EXIT CONDITIONS:
* 
*          TASK CONTEXT SAVED.EXIT TO DISPATCHER
* 
**********************************************************************/
NEWTASK:
           MOVE.L     CURRENT,%A0         /* %A0-> CURRENT TCB     */
           CMPA.L     #0,%A0              /* CHECK TO MAKE SURE THERE IS ONE    */
           BEQ        DISPATCH           /* EQ=>NO CURRENT TASK    */
           /* MOVE.L     %USP,%A1  */           /* FETCH USER STSCK POINTER     */
           MOVE.L     %A1,T_USP(%A0)       /* SAVE IN TCB    */
           MOVE.L     %A7,T_SSP(%A0)       /* SAVE SUPERVISOR STACK POINTER    */
           CLR.L      CURRENT            /* INDICATE NO ACTIVE TASK    */
/* 
*      HERE WE TRY TO DISPATCH THE HIGHEST PRIORITY TASK.IF NONE,WAIT 
*      UNTIL THERE IS ONE 
*/ 
DISPATCH:
           MOVE.L     TINDEX,%D2           /* ***?*** TASK COUNT STILL IN USE? */
           ADDQ.L     #1,%D2
           MOVE.L     %D2,TINDEX 
           CMPI.L     #6,%D2
           BNE        GO_ON 
           CLR.L      TINDEX 
GO_ON:
           MOVE.L     TINDEX,%D0 
           ASL.L      #2,%D0 
           MOVEA.L    #TASKTAB,%A0 
           MOVE.L     0(%A0,%D0.L),%A0
           TST.L      T_FLAG(%A0)
           BNE        DISPATCH
  
           MOVE.L     %A0,CURRENT            /* SET CURRENT TASK     */
           MOVE.L     T_USP(%A0),%A1         /*  A1=USER STACK POINTER    */
           /* MOVE.L     %A1,%USP */               /* SET USP    */
           MOVE.L     T_SSP(%A0),%A7         /* SET KERNEL STACK POINTER    */
DORTE: 
           MOVEM.L    (%A7),%D0-%D7/%A0-%A6  /* RESTORE TASK'S REGISTERS    */
           RTE                               /* (RE) ENTER TASK CODE    */
/* 
*    HERE IF NO READY TASKS.ENABLE INTERRUPTS AND WAIT. 
*/ 
NONE:
           STOP       #0x2000                /* WAIT FOR INTERRUPT    */
           BRA        DISPATCH               /* TRY TO DISPATCH AGAIN    */
/**********************************************************************
* 
*          L B O S  D A T A   A R E A 
* 
*    THIS AREA CONTAINS THE DATA USED BY THE OPERATING SYSTEM.
* 
**********************************************************************
*                                        INITIALIZED DATA 
* 
*          DATA FOR INITIALIZATION ROUTINE
*/ 
INITDATA:
           .LONG       TASK1,0,TASK1EPA,0x2000
           .LONG       TASK2,0,TASK2EPA,0x2000
           .LONG       TASK3,0,TASK3EPA,0x2000
           .LONG       TASK4,0,TASK4EPA,0x2000
  
           .LONG       0                       /* END OF LIST MARKER    */
  
/**********************************************************************
*          ***?***
*   I N T E R R U P T   P O L L I N G    R O U T I N E
* 
*          ROUTINE "POLLINT"DETERMINES THE SOURCE OF A LEVEL 1
*          AUTOVECTOR INTERRUPT.THE STANDARD STACK ARRANGEMENT IS 
*          SET UP HERE. 
* 
**********************************************************************/
           .TEXT
POLLINT:
           MOVEM.L    %D0-%D7/%A0-%A6,(%A7)  /* SAVE REGISTERS    */
         /*  MOVE.B     #RDVEC,POLL   */     /* ISSUE "READ VECTOR" COMMAND    */
         /*  MOVE.B     POLL,%D0      */      /* FETCH VECTOR     */
         /*  MOVE.B     #CLRINT,POLL  */     /* INDICATE END OF INTERRUPT    */
         /*  TST.B      %D0           */      /* INTERRUPT PENDING?     */
         /*  BPL        NOINT         */     /* OOPS! NO INTERRUPT AT ALL    */
         /*  ANDI.W     #7,%D0        */      /* CLEAR ALL BUT VECTOR BITS    */
         /*  LSL.W      #2,%D0        */      /* MULTIPLY BY 4    */
        /*  LEA        VECTABLE,%A0   */     /* %A0 -> VECTOR ADDRESSES     */
        /*   MOVE.L     0(%A0,%D0.W),%A0 */   /*   %A0 -> ROUTINE    */
        /*   JMP        (%A0)           */    /* ENTER INTERRUPT ROUTINE    */
NOINT:
           MOVEM.L    (%A7),%D0-%D7/%A0-%A6  /* RESTORE REGISTERS    */
           RTE                           /* EXIT TO INTERRUPTED ROUTINE    */
  
VECTABLE:
           .LONG       TIMEINT            /* 0 TIMER    */
           .LONG       NOINT              /* 1 REMOTE INPUT     */
           .LONG       TERMINT            /* 2 TERMINAL OUTPUT INTERRUPT    */
           .LONG       NOINT              /* 3 REMOTE OUTPUT    */
           .LONG       NOINT              /* 4 REMOTE RING INDICATOR    */
           .LONG       NOINT              /* 5 CENTRONICS     */
           .LONG       NOINT              /* 6 REAL-TIME CLOCK    */
           .LONG       NOINT              /* 7 SOFTWARE CONTROLLED    */
/* 
**********************************************************************
*                     S E N D   M E S S A G E   S V C 
* 
*          ROUTINE "SEND" IS ENTERED VIA TRAP OR A JUMP FROM ANOTHER
*          SVC HANDLER. THIS ROUTINE ALLOCATES A MESSAGE CONTROL BLOCK
*          AND MARKS THE TASK AS NON-DISPATCHABLE. IT CLEARS THE RWAIT
*          (RECEIVE WAIT) BIT IN THE TARGET TASK.
* 
**********************************************************************/
SEND:
           MOVE.W     #DISABLE,%SR        /* DISABLE TO ALLOCATE MCB    */
           MOVE.L     MCBLIST,%A6         /* %A6 -> FIRST MESSAGE BLOCK    */
           CMPA.L     #0,%A6              /* ARE THERE ANY?     */
           BEQ        NOMCBS             /* NO, RETURN AN ERROR    */
           MOVE.L     (%A6),MCBLIST       /* REMOVE BLOCK FROM LIST    */
           MOVE.W     #ENABLE,%SR         /* ALLOW INTERRUPTS     */
           CLR.L      M_LINK(%A6)         /* CLEAR LINK WORD    */
           MOVE.L     %A0,M_BUFF(%A6)     /*  PUT BUFFER ADDRESS INTO MCB    */
           MOVE.L     %D1,M_COUNT(%A6)    /*  PUT COUNT INTO MCB    */
           MOVE.L     CURRENT,%A1         /* %A1 -> CURRENT TASK     */
           MOVE.L     %A1,M_RTCB(%A6)     /*  SAVE TASK'S TCB ADDRESS    */
           CMP.L      #MAXTASK,%D2        /* IS %D2 IN RANGE?    */
           BHI.W      BADTASK            /* NO, DON'T GO ANY FURTHER     */
           BSET.B     #MWAIT,T_FLAG+3(%A1)  /* MAKE TASK WAIT FOR REPLY    */
           ASL.L      #2,%D2              /* MULTIPLY TASK INDEX BY 4     */
           LEA        TASKTAB,%A2         /* %A2 -> TASKTABLE    */
           ADDA.L     %D2,%A2              /* %A2 -> ADDRESS OF TCB ADDRESS    */ 
           MOVE.L     (%A2),%A2            /* %A2 -> TARGET TCB    */
           LEA        T_MSG(%A2),%A3       /* %A3 -> MESSAGE LIST    */
           MOVE.W     #DISABLE,%SR        /* DISABLE FOR QUEUE SEARCH     */
GETMCB:
           TST.L      M_LINK(%A3)         /* ZERO LINK?    */
           BEQ        MCBEND             /* YES, ADD IT AT END     */
           MOVE.L     M_LINK(%A3),%A3      /* ADVANCE TO NEXT LIST POSITION    */
           BRA        GETMCB             /* CONTINUE UNTIL FIND NULL LINK    */
MCBEND:
           MOVE.L     %A6,M_LINK(%A3)      /* LINK MCB IN AT END    */
           MOVE.W     #ENABLE,%SR         /* RE-ALLOW INTERRUPTS    */
           BCLR.B     #RWAIT,T_FLAG+3(%A2)  /* INDICATE TASK DISPATCHABLE    */
           BRA        NEWTASK            /* GO TO NEW TASK     */
/* 
*          HERE ON ERROR
*/ 
BADTASK:
           MOVE.L     #E_BADTASK,(%A7)    /* MODIFY DO ON STACK    */
           BRA        DORTE              /* GO BACK TO TASK CODE    */ 
NOMCBS:
           MOVE.L     #E_NOMCB,(%A7)      /* MODIFY %D0 ON STACK    */
           BRA        DORTE              /* GO BACK TO TASK CODE     */

/* SCHEDULING ORDER IS DEFINED BY THE FOLLOWING TABLE */
TASKTAB:
           .LONG       TASK1,TASK2,TASK3,TASK2,TASK4,TASK2 
           .EQU        MAXTASK,(.-TASKTAB)/4 

           .EQU        I_TIME,0                  /* TIMER TASK INDEX     */
           .EQU        I_TERM,1                  /* TERMINAL TASK INDEX    */

           .EQU        TIMETASK,. 
           .EQU        TERMTASK,. 
/**********************************************************************
* 
*          R E C E I V E   M E S S A G E   S V C
* 
*          ROUTINE "RECV" IS USED BY A TASK DESIRING TO RECEIVE A 
*          MESSAGE FROM ANOTHER TASK. INCOMING MESSAGES ARE QUEUED
*          UP IN FIFO ORDER BY "SEND". THE MESSAGE IS REMOVED FROM
*          THE "T_MSG" LIST AND PLACED IN THE "T_RPLY" LIST.
* 
**********************************************************************/
RECV:
           MOVE.L     CURRENT,%A0         /* %A0 -> CURRENT TASK'S TCB     */
           MOVE.W     #DISABLE,%SR        /* DISABLE INTERRUPTS     */
           TST.L      T_MSG(%A0)          /* ANY MESSAGES WAITING    */
           BEQ        NOMSGS             /* NO, HAVE TO WAIT FOR ONE    */ 
           MOVE.L     T_MSG(%A0),%A1       /* %A1 -> FIRST MESSAGE    */
           MOVE.L     M_LINK(%A1),T_MSG(%A0)  /* REMOVE FROM T_MSG LIST    */
           MOVE.L     T_RPLY(%A0),M_LINK(%A1) /* PUT ON    */
           MOVE.L     %A1,T_RPLY(%A0)                  /* REPLY LIST    */
           MOVE.L     %A1,(%A7)           /*  REPLACE %D0 CONTENTS    */
           BRA        DORTE              /* GO BACK TO TASK    */
NOMSGS:
           BSET.B     #RWAIT,T_FLAG+3(%A0)   /* MARK WAITING FOR MESSAGE    */
           MOVE.L     #SVCNEW,%D0         /* ISSUE NEW TASK     */
           TRAP       #0                                        /* SVC     */
           BRA        RECV               /* TRY RECEIVE AGAIN    */
/* 
**********************************************************************
* 
*          R E P L Y   T O   M E S S A G E   S V C
* 
*          ROUTINE "REPLY" CAUSES THE TASK WHICH ORIGINALLY SENT A
*          MESSAGE TO RESUME EXECUTION, WITH %D0 SET .EQUAL TO THE
*          RETURNED STATUS CODE FROM THE TASK WHICH RECEIVED AND
*          PROCESSED THE MESSAGE. 
* 
**********************************************************************/
REPLY:
           MOVE.L     CURRENT,%A2         /* %A2 -> CURRENT TASK TCB     */
           LEA        T_RPLY(%A2),%A1      /* %A1 -> LIST HEADER    */
           MOVE.W     #DISABLE,%SR        /* NO INTERRUPTS IN SEARCH    */
FNDMCB:
           TST.L      M_LINK(%A1)         /* END OF LIST?    */
           BEQ        BADMCB             /* YES, RETURN ERROR    */
           CMP.L      M_LINK(%A1),%A0      /* DOES LINK POINT TO RIGHT ONE?    */
           BEQ        FOUND              /* YES, TAKE OUT OF LIST    */
           MOVE.L     M_LINK(%A1),%A1      /* ADVANCE THE LIST    */
           BRA        FNDMCB             /* AND TRY AGAIN    */
FOUND:
           MOVE.L     M_LINK(%A0),M_LINK(%A1)   /* TAKE OUT OF LIST    */
           MOVE.L     M_RTCB(%A0),%A3     /*  %A3 -> SENDER'S TCB    */
           MOVE.L     T_SSP(%A3),%A4      /*  %A4 -> SENDER'S %D0    */
           MOVE.L     M_STAT(%A1),(%A4)    /* PUT IN STATUS CODE    */
           BCLR.B     #MWAIT,T_FLAG+3(%A3)   /* MARK DISPATCHABLE    */
           MOVE.L     MCBLIST,%D3
           MOVE.L     %D3,M_LINK(%A0)    /* PUT MCB    */
           MOVE.L     %A0,MCBLIST                     /* BACK IN FREE LIST    */
           MOVE.W     #ENABLE,%SR         /* ENABLE INTERRUPTS    */
           BRA        NEWTASK            /* AND RE-DISPATCH    */
BADMCB:
           MOVE.L     #E_BADMCB,(%A7)     /* SET ERROR CODE    */
           BRA        DORTE              /* RETURN TO OFFENDER     */
/* 
**********************************************************************
* 
*          T E R M I N A L   O U T P U T   S V C
* 
*          ROUTINE "PRTLINE" CAUSES A BUFFER FULL OF OUTPUT TO BE SENT
*          TO THE TERMINAL. THE TASK IS SUSPENDED UNTIL THE BUFFER IS 
*          OUTPUT.
* 
**********************************************************************/
PRTLINE:
           MOVE.L     #I_TERM,%D2         /* SET APPROPRIATE INDEX    */
           BRA        SEND               /* AND DO SEND MESSAGE    */
/* 
**********************************************************************
* 
*                     D E L A Y   S V C 
* 
*          ROUTINE "DELAY" CAUSES THE ISSUING TASK TO BE DELAYED BY 
*          THE NUMBER OF CLOCK TICKS CONTAINED IN REGISTER %D1.
* 
**********************************************************************/
DELAY:
           MOVE.L     #I_TIME,%D2         /* LOAD TASK INDEX    */
           BRA        SEND               /* AND DO A SEND SVC    */
/* 
**********************************************************************
* 
*                     T I M E R   T A S K 
* 
**********************************************************************/
TIMEEPA:
           MOVE.L     #SVCRCV,%D0           /* RECEIVE A MESSAGE    */
           TRAP       #0                    /* FROM USER TASK R.EQUESTING DELAY    */
           MOVE.L     %D0,%A0               /* %A0 -> MCB FOR R.EQUEST    */
           MOVE.L     M_COUNT(%A0),%D3
           MOVE.L     %D3,TCOUNT            /* COPY COUNT FOT INTERRUPT RTN    */
           MOVE.L     CURRENT,%A1           /* %A1 -> OUR TCB    */
           MOVE.W     #DISABLE,%SR          /* DISABLE INTERRUPTS     */
           MOVE.L     TSTATUS,%D3
           BSET.B     #0,%D3                /* TURN ON INTERRUPT FLAG     */
           MOVE.L     %D3,TSTATUS
           BSET       #IWAIT,T_FLAG+3(%A1)  /* MAKE OURSELVES    */
/*                                          /* NON-DISPATCHABLE */
           BSR        TRSET                 /* LOAD TIMER REGISTERS     */
           MOVE.L     #SVCNEW,%D0           /* SWITCH TO NEXT     */
           TRAP       #0                                /* TASK    */
           MOVE.W     #ENABLE,%SR           /* RE-ENABLE INTERRUPTS    */ 
           MOVE.L     #SVCRPLY,%D0          /* REPLY TO MCB     */
           TRAP       #0                    /* ISSUE REPLY SVC    */
           BRA        TIMEEPA               /* GO AROUND AGAIN    */
/* 
*          THE FOLLOWING CODE IS ENTERED VIA TIMER INTERRUPTS 
*/ 
TIMEINT:
           MOVE.L     TSTATUS,%D3
           BTST.L     #0,%D3             /* IS TIMING DESIRED?     */
           BNE        TPROC              /* NE => YES, DECREMENT COUNT     */
           BRA        NOINT              /* NO, JUST EXIT    */
TPROC:
           SUBQ.L     #1,TCOUNT          /* DECREMENT COUNTER    */
           BEQ        TWAKE              /* E0 => DELAY FINISHED        */
           BSR        TRSET              /* RELOAD COUNT REGISTERS     */
           BRA        NOINT              /* OTHERWISE, DELAY SOME MORE     */
TWAKE:
            
           BCLR.L     #0,%D3
           MOVE.L     %D3,TSTATUS         /* CLEAR TIMER ACTIVE FLAG    */
           LEA        TIMETASK,%A0        /* %A0 -> OUR TCB    */
           MOVE.L     T_FLAG+3(%A0),%D3
           BCLR.L     #IWAIT,%D3
           MOVE.L     %D3,T_FLAG+3(%A0)  /* MAKE TASK PART DISPATCHABLE    */
                                         /* DISABLE TIMER INTERRUPT   ***?*** */
           BRA        NEWTASK            /* ENTER DISPATCHER     */
/* 
*          TIMER RESET ROUTINE
*/ 
TRSET:
             /* ***?*** */               /* DISABLE TIMER INTERRUPT ***?***   */
                                         /* SET UP     */
                                         /* TIMER    */
                                         /* LOAD LOW COUNT BYTE     */
                                         /* LOAD HIGH COUNT BYTE    */
                                         /* LOAD NEXT COUNT STAGE    */
                                         /* LOAD HIGH BYTE     */
                                         /* ENABLE CLOCK INTERRUPTS   ***?*** */
           RTS

           .DATA
TCOUNT:    .SKIP      4                  /* NUMBER OF TICKS REMAINING    */
TSTATUS:   .SKIP      4                  /* STATUS WORD    */


           .TEXT
/**********************************************************************
* 
*          T E R M I N A L   O U T P U T   T A S K
* 
*          THIS CODE PERFORMS TERMINAL OUTPUT FOR OTHER TASKS 
* 
**********************************************************************/
TERMEPA:
           MOVE.L     #SVCRCV,%D0         /* RECEIVE OUTPUT     */
           TRAP       #0                  /*                R.EQUEST    */
           MOVE.L     %D0,%A0             /* %A0 -> MCB    */
           MOVE.L     M_COUNT(%A0),%D3
           MOVE.L     %D3,BCOUNT          /*  MAKE LOCAL COPY OF    */
           MOVE.L     M_BUFF(%A0),%A2              /*              REQ.DATA    */
           SUBQ.L     #1,BCOUNT           /* DECREMENT COUNT    */
           MOVE.L     CURRENT,%A1         /* %A1 -> OUR TCB    */
           MOVE.W     #DISABLE,%SR        /* DISABLE INTERRUPTS FOR STARTUP     */
           BSET.B     #IWAIT,T_FLAG+3(%A1)  /* MARK US NOT DISPATCHABLE    */
                                           /* ENABLE TERMINAL INTERRUPT    */
           MOVE.B     (%A2)+,TERMOUT      /* OUTPUT FIRST CHARACTER    */
           MOVE.L     %A2,BUFF            /* REMEMBER BUFFER ADDRESS    */
           MOVE.L     #SVCNEW,%D0         /* EXECUTE    */
           TRAP       #0                  /*            NEXT TASK    */
           MOVE.W     #ENABLE,%SR         /* RE-ENABLE INTERRUPTS     */
           MOVE.L     #SVCRPLY,%D0        /* REPLY TO MCB     */
           TRAP       #0                  /* ISSUE REPLY SVC    */
           BRA        TERMEPA             /* DO NEXT BUFFER    */ 
/* 
*          THE FOLLOWING CODE IS ENTERED WHEN EACH CHARACTER IS OUTPUT
*/ 
TERMINT:
           TST.L      BCOUNT              /* ARE WE DONE?     */
           BGT        NEXTCHAR            /* NO, CONTINUE TO OUTPUT    */ 
                                          /* DISABLE TERMINAL INTERRUPT     */
           LEA        TERMTASK,%A0        /* %A0 -> OUR TCB    */
           BCLR.B     #IWAIT,T_FLAG+3(%A0)   /* CLEAR WAIT BIT    */
           BRA        NEWTASK             /* GO FIND A NEW TASK    */ 
NEXTCHAR:
           MOVE.L     BUFF,%A0            /* %A0 -> CHARACTER TO OUTPUT    */
           MOVE.B     (%A0)+,TERMOUT      /* OUTPUT THE CHARACTER    */
           MOVE.L     %A0,BUFF            /* SAVE NEXT ADDRESS    */
           SUBQ.L     #1,BCOUNT           /* DECREMENT COUNT    */
           BRA        NOINT               /* RESUME INTERRUPTED TASK    */
/* 
*          DATA AREA
*/ 
           .DATA
BCOUNT:    .SKIP      4                   /* BYTE COUNT TO OUTPUT     */
BUFF:      .SKIP      4                   /* BUFFER ADDRESS TO OUTPUT     */

/*         END OF LBOS SYSTEM CODE        */
            

/* 
*          DUMMY TASKS
*/
TASK1EPA:
          SUBQ.L      #1,%D0
          BRA         TASK1EPA
/*            */
TASK2EPA:
          SUBQ.L      #1,%D0
          BRA         TASK2EPA
/*             */
TASK3EPA:
          SUBQ.L      #1,%D0
          BRA         TASK3EPA
/*             */
TASK4EPA:
          SUBQ.L      #1,%D0
          BRA         TASK4EPA
/*             */





